package com.lagou.edu.factory;

import com.alibaba.druid.util.StringUtils;
import com.lagou.edu.anotation.Autowired;
import com.lagou.edu.anotation.Service;
import com.lagou.edu.anotation.Transactional;
import org.reflections.Reflections;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author 应癫
 * <p>
 * 工厂类，生产对象（使用反射技术）
 */
public class BeanFactory {

    /**
     * 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */

    private static Map<String, Object> map = new HashMap<>();  // 存储对象

    static {
        try {
            //任务一、扫描包，通过反射技术实例化对象并且存储待用（map集合）

            //替换从xml配置文件中解析实例化对象信息 用Reflections反射获取到被扫描包下的对象信息方式
            Reflections edus = new Reflections("com.lagou.edu");
            Set<Class<?>> clazzs = edus.getTypesAnnotatedWith(Service.class);
            //遍历对象集合 获取需要实例化对象
            for (Class<?> clazz : clazzs) {
                Object object = clazz.newInstance();
                Service service = clazz.getAnnotation(Service.class);

                //判断Service注解上是否存在自定义对象ID
                if (StringUtils.isEmpty(service.name())) {
                    //获取的是全限定类名
                    String name = clazz.getName();
                    //获取类名
                    int i = name.lastIndexOf(".");
                    String substring = name.substring(i+1, name.length());
                    map.put(substring, object);
                } else {
                    map.put(service.name(), object);
                }
            }
            //管理对象之间依赖关系
            for (Map.Entry<String, Object> entrySet : map.entrySet()) {
                Object obj = entrySet.getValue();
                Class clazz = obj.getClass();
                //获取类的所有属性
                Field[] fields = clazz.getDeclaredFields();
                //筛选使用Autowired注解的属性，并完成其依赖对象的注入
                for (Field field : fields) {
                    //判断是否使用注解的参数
                    if (field.isAnnotationPresent(Autowired.class)
                        && field.getAnnotation(Autowired.class).required()) {//有使用注解则注入
                        String name =  field.getType().getName();
                        int i = name.lastIndexOf(".");
                        name = name.substring(i+1, name.length());
                        Method[] methods = clazz.getMethods();
                        for (int j = 0; j < methods.length; j++) {
                            Method method = methods[j];
                            if (method.getName().equalsIgnoreCase("set" + name)) {  // 该方法就是 setAccountDao(AccountDao accountDao)
                                method.invoke(obj, map.get(name));
                            }
                        }
                    }
                }

                //处理事务管理
                for (final Method declaredMethod : clazz.getDeclaredMethods()) {
                    if (declaredMethod.getAnnotation(Transactional.class) != null) {
                        //获取代理工厂
                        //筛选被Transactional注解修饰的类，并使用代理对象进行事务管理
                        ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
                        Class[] face = clazz.getInterfaces();//获取当前类实现的所有接口
                        //判断代理方式 ，实现接口的对象采用jdk动态代理方式，未实现接口采用cjlab动态代理方式
                        if (face != null && face.length > 0) {
                            //实现使用JDK
                            obj = proxyFactory.getJdkProxy(obj);
                        } else {
                            //没实现使用CGLIB
                            obj = proxyFactory.getCglibProxy(obj);
                        }
                    }
                }
                // 把处理之后的object重新放到map中
                map.put(entrySet.getKey(), obj);

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static Object getBean(String id) {
        return map.get(id);
    }
}
